package com.suemi.rpc.util;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SerializationUtil {

  private static final Map<Class<?>, Schema<?>> schemas = new ConcurrentHashMap<>();

  private static final Objenesis objenesis = new ObjenesisStd(true);

  @SuppressWarnings("unchecked")
  public static <T> byte[] serialize(T obj) {
    Class<T> clazz = (Class<T>) obj.getClass();
    LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);

    try {
      Schema<T> schema = getSchema(clazz);
      return ProtobufIOUtil.toByteArray(obj, schema, buffer);
    } catch (Exception e) {
      throw new IllegalStateException(e.getMessage(), e);
    } finally {
      buffer.clear();
    }
  }

  @SuppressWarnings("unchecked")
  public static <T> T deserialize(byte[] data, Class<T> clazz) {
    try {
      T instance = objenesis.newInstance(clazz);
      Schema<T> schema = getSchema(clazz);
      ProtobufIOUtil.mergeFrom(data, instance, schema);
      return instance;
    } catch (Exception e) {
      throw new IllegalStateException(e.getMessage(), e);
    }
  }

  @SuppressWarnings("unchecked")
  private static <T> Schema<T> getSchema(Class<T> clazz) {
    if (schemas.containsKey(clazz)) return (Schema<T>) schemas.get(clazz);
    Schema<T> schema = RuntimeSchema.getSchema(clazz);
    schemas.put(clazz, schema);
    return schema;
  }
}
